<script setup lang="ts" generic="T">
interface Props {
  // 是否正在加载
  loading: boolean
  // 是否已加载完成
  finished: boolean
  // 是否加载出错
  error?: boolean
  // 触发加载的距离阈值(像素)
  distance?: number
  // 加载提示位置
  loadingPosition?: 'bottom' | 'center'
  // 完成提示位置
  finishedPosition?: 'bottom' | 'center'
  // 错误提示位置
  errorPosition?: 'bottom' | 'center'
  // 是否显示状态提示的边距
  hasMargin?: boolean
}

withDefaults(defineProps<Props>(), {
  distance: 100,
  loadingPosition: 'bottom',
  finishedPosition: 'bottom',
  errorPosition: 'center',
  error: false,
  hasMargin: false
})

const emit = defineEmits<{
  retry: [void]
}>()
</script>

<template>
  <div>
    <div
      v-if="loading"
      class="loading-indicator"
      :class="[`position-${loadingPosition}`, { 'has-margin': hasMargin }]"
    >
      <slot name="loading">
        <div class="default-loading">
          <div class="spinner" />
          <span>加载中...</span>
        </div>
      </slot>
    </div>
    <div
      v-if="finished"
      class="finished-indicator"
      :class="[`position-${finishedPosition}`, { 'has-margin': hasMargin }]"
    >
      <slot name="finished">
        <div class="default-finished">没有更多数据了</div>
      </slot>
    </div>
    <div
      v-if="error"
      class="error-indicator"
      :class="[`position-${errorPosition}`, { 'has-margin': hasMargin }]"
    >
      <slot name="error">
        <div class="default-error">
          <span>加载失败</span>
          <button @click="emit('retry')">点击重试</button>
        </div>
      </slot>
    </div>
  </div>
</template>

<style scoped>
.sentinel {
  /* position: absolute;
  bottom: 0; */
  width: 100%;
  pointer-events: none;
  border: 1px solid var(--my-color-error-base);
}

.loading-indicator,
.finished-indicator,
.error-indicator {
  /* padding: 16px 0; */
  text-align: center;
  color: var(--my-color-text-1);
}

.loading-indicator.position-center,
.finished-indicator.position-center,
.error-indicator.position-center {
  position: absolute;
  bottom: 50%;
  right: 0;
  left: 0;
}

/* 使用虚拟列表时 */
.loading-indicator.position-useVirtual,
.finished-indicator.position-useVirtual,
.error-indicator.position-useVirtual {
  position: absolute;
  bottom: 0%;
  right: 0;
  left: 0;

  /* transform: translateY(-50%); */
}

.loading-indicator.has-margin,
.finished-indicator.has-margin,
.error-indicator.has-margin {
  margin: 16px 0;
}

.default-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}

.spinner {
  width: 20px;
  height: 20px;
  border: 2px solid var(--my-color-mask-1);
  border-top-color: var(--my-color-primary-base);
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

.default-finished {
  padding: 12px;
  color: var(--my-color-text-1);
}

.default-error {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}

.default-error button {
  padding: 6px 16px;
  color: var(--my-color-text-5);
  background-color: var(--my-color-error-base);
  border: none;
  border-radius: 4px;
  transition: background-color 0.3s;
  cursor: pointer;
}

.default-error button:hover {
  background-color: var(--my-color-error-5);
}
</style>
